package com.nikolaj.kuzan.controller;

import com.nikolaj.kuzan.utils.DataComparator;
import com.nikolaj.kuzan.utils.DataTransformer;
import com.nikolaj.kuzan.utils.DoubleConverter;
import com.nikolaj.kuzan.utils.Printer;
import org.neuroph.core.data.DataSet;
import org.neuroph.core.data.DataSetRow;
import org.neuroph.nnet.Hopfield;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;

public class Controller {

    private final int IMG_SIZE;
    private DataTransformer dataTransformer = new DataTransformer();
    private Double[] inputVector;
    private Double[] networkOutput;
    private List<Double[]> inputVectors;

    private Hopfield myHopfield;
    private DataSet trainingSet;
    private List<Double[]> trainingList;

    private List<Integer> randomList;
    private Map<Integer, Integer> linkPercentWithImageSize;
    private Map<Integer, Integer> timeOfCalculatingMap = new TreeMap<>();


    public Controller() {
        linkPercentWithImageSize = new HashMap<>();
        linkPercentWithImageSize.put(0, 32);
        linkPercentWithImageSize.put(6, 31);
        linkPercentWithImageSize.put(12, 30);
        linkPercentWithImageSize.put(18, 29);
        IMG_SIZE = 32;
    }

    public Map<Integer, Integer> getTimeOfCalculatingMap() {
        return timeOfCalculatingMap;
    }

    public Double[] imgFileToInputVector(File file, boolean croppImage, int imageSize) throws IOException {

        BufferedImage image = ImageIO.read(file);
        inputVector = dataTransformer.imgToInputVector(image, croppImage, imageSize);
        return inputVector;

    }

    public List<Double[]> imgFilesToInputVectors(List<File> files, boolean croppImage, int imageSize) throws IOException {
        inputVectors = new ArrayList<>();
        for (File tempFile : files) {
            inputVectors.add(imgFileToInputVector(tempFile, croppImage, imageSize));
        }
        return inputVectors;
    }

    public void addInputVectorToTrainingSet() {
        //if training set isn't exist then create new training set
        if (trainingSet == null) {
            trainingSet = new DataSet(inputVector.length);
        }

        double[] dInputVector = DoubleConverter.objectDoubleArrToDoubleArr(inputVector);
        //add training element to exist training set
        trainingSet.addRow(new DataSetRow(dInputVector));
    }

    public void addInputVectorsToTrainingSet() {
        if (inputVector == null){
            throw new RuntimeException("Input vector is empty");
        }

        if (trainingSet == null) {
            trainingSet = new DataSet(inputVector.length);
        }

        for (Double[] vector : inputVectors) {
            double[] dInputVector = DoubleConverter.objectDoubleArrToDoubleArr(vector);
            trainingSet.addRow(new DataSetRow(dInputVector));
        }
    }

    public void trainNetwork() {

        // create hopfield network
        if (myHopfield == null && inputVector != null) {
            myHopfield = new Hopfield(inputVector.length);
        } else if (myHopfield == null && inputVectors != null) {
            myHopfield = new Hopfield(inputVectors.get(0).length);
        } else {
            //throw new RuntimeException("Cann't create training set");
        }

        // learn the training set
        int countOfLearningCycles = 40;
        for (int i = 0; i < countOfLearningCycles; i++){
            myHopfield.learn(trainingSet);
        }
    }

    public Double[] testNetwork(File file, boolean croppImage, int percentOfDeletedNeurons, DeleteFrom deleteFrom) throws IOException {

        System.out.println();
        System.out.println("Testing network");
        System.out.println("Original image from panel - 32x32");

        int newImgSize = 32;//linkPercentWithImageSize.get(percentOfDeletedNeurons);
        Double[] input = imgFileToInputVector(file, croppImage, newImgSize);


/*
        List<Double> tempList = new ArrayList<>(Arrays.asList(input));


        for (int i = 0; i < randomList.size(); i++){
            if (randomList.get(i) < tempList.size()-1){
                tempList.set(randomList.get(i)+1, (tempList.get(randomList.get(i))+tempList.get(randomList.get(i)+1))/2);
            }
            tempList.remove(randomList.get(i).intValue());
        }

        input = new Double[tempList.size()];
        tempList.toArray(input);
*/

        double[] dInput = DoubleConverter.objectDoubleArrToDoubleArr(input);

        System.out.println(dInput.length);
        System.out.println(myHopfield.getLayerAt(0).getNeuronsCount());

        myHopfield.setInput(dInput);

        Date date = new Date();

        long beginOfCalculating = System.currentTimeMillis();
        myHopfield.calculate();
        myHopfield.calculate();
        long endOfCalculating = System.currentTimeMillis();

        long calcDuration = (endOfCalculating - beginOfCalculating);
        System.out.println("CALCULATION DURATION - " + calcDuration);

        int countOfDeletedNeurons = randomList.size();
        timeOfCalculatingMap.put(countOfDeletedNeurons, (int) calcDuration);

        networkOutput = DoubleConverter.doubleArrToObjectDoubleArr(myHopfield.getOutput());

        System.out.println("-------------------NETWORK OUTPUT-----------------");
        System.out.print("output in controller - " + networkOutput.length);
        Printer.printImg(networkOutput, newImgSize);
        System.out.println("-*-*-*-*-*-*-*-*-NETWORK OUTPUT-*-*-*-*-*-*-*-*-*-*-*");


        if (randomList != null){
            for (int position : randomList){
                if (position < networkOutput.length - 1){
                    networkOutput[position] = -1.0;
                }
            }
        }
        return networkOutput;
    }

    public List<Double> compareVectors(List<File> trainingFiles, Double[] networkOutput, int percentOfDeletedNeurons) throws IOException {

        int newImgSize = 32;//linkPercentWithImageSize.get(percentOfDeletedNeurons);

        System.out.println();
        System.out.println("-------------OUTPUT WILL BE COMPARED WITH NEXT IMAGES---------------");
        //trainingList = imgFilesToInputVectors(trainingFiles, false, newImgSize);
        System.out.println("//-----------OUTPUT WILL BE COMPARED WITH NEXT IMAGES-------------//");

        return DataComparator.compareVectors(trainingList, networkOutput);
    }

    public List<Double> compareVectors(List<Double[]> originalVectors, Double[] noisedVector){
        List<Double> result = DataComparator.compareVectors(originalVectors, noisedVector);
        return result;
    }

    public void deleteNeurons(int percentageOfNeurons, DeleteFrom deleteFrom){

        //TODO добавить проверку для всех значений, введенных пользователем (не только те, которые корректны)

//        int newImgSize = linkPercentWithImageSize.get(percentageOfNeurons);
        int countOfNeuronsForDelete = (int) Math.round((IMG_SIZE*IMG_SIZE)/100.0*percentageOfNeurons);
        //IMG_SIZE * IMG_SIZE -  newImgSize * newImgSize;
        //--------------------------------------------------------
        Random random = new Random();
        int randomVal;


        randomList = new ArrayList<>();
        for (int i = 0; i < countOfNeuronsForDelete; i++){
            randomVal = random.nextInt(myHopfield.getLayerAt(0).getNeuronsCount());
            randomList.add(randomVal);
            //myHopfield.getLayerAt(0).removeNeuronAt(randomVal);

            myHopfield.getLayerAt(0).getNeuronAt(randomVal).removeAllOutputConnections();
            //System.out.print(myHopfield.getLayerAt(0).getNeuronAt(randomVal).getNetInput() + " - ");
            //System.out.println(myHopfield.getLayerAt(0).getNeuronAt(randomVal).getOutput() + ";");
        }
        //-------------------------------------------------------

        double[] dNewWeightMatrix = DoubleConverter.objectDoubleArrToDoubleArr(myHopfield.getWeights());


        //myHopfield = new Hopfield(myHopfield.getLayerAt(0).getNeuronsCount());
        //myHopfield.setWeights(dNewWeightMatrix);

        //-------------------------------PRINTING TO CONSOLE-------------------------------------------------//
        System.out.println("neurons count in deleteNeurons - " + myHopfield.getLayerAt(0).getNeuronsCount());
        //---------------------------------END OF PRINTING---------------------------------------------------//


        trainingList = new ArrayList<>();
        for (int i = 0; i < trainingSet.size(); i++) {

            double[] tempArr = trainingSet.getRowAt(i).getInput();
            Double[] DTempArr = DoubleConverter.doubleArrToObjectDoubleArr(tempArr);

            List<Double> tempList = new ArrayList<>(Arrays.asList(DTempArr));
            for (int j = 0; j < countOfNeuronsForDelete; j++){
               // tempList.remove(randomList.get(j).intValue());
            }

            DTempArr = new Double[tempList.size()];
            tempList.toArray(DTempArr);

            trainingList.add(DTempArr);
        }

    }

    public BufferedImage imgVectorToImg(Double[] vector){
        return dataTransformer.imgVectorToImg(vector, IMG_SIZE);
    }
}




